﻿#pragma once

#include <iostream>
#include "utils/OutputUtils.h"

/** 
# Execution Rules

## A Execution Contains
1. The **Explicit** **[Promise]** Object
2. The **Explicit** **[Coroutine Handle]** which like `std::coroutine_handle<T>`
3. The **Implicit** **[Coroutine State]** dynamically-allocated storage, contains
    1. The **[Promise]** Object
    2. The **[Input Parameters]** Copied by Value

## A Execution Behavior
1. new **[Coroutine State]**
2. copy **[Function Params]** to **[Coroutine State]**
3. construct **[Promise]**
    1. Constructor matching **[Function Params]**
    2. or default constructor
4. call **[Promise get_return_object()]**, compiler will find return object by **promise_type**
    1. exception throws to caller
5. call **[Promise initial_suspend()]**, equals **[co_await promise.initial_suspend()]**
6. during execution
    1. exec each **[co_await Awaiter]** as suspension point
        1. call **[Awaiter await_ready]**
        2. call **[Awaiter await_suspend]**
        3. call **[Awaiter await_resume]** when handle.resume()
    2. exec each **[co_return or co_return expr]** as suspension point
        1. call **[Promise return_void or return_value]**
    3. throw exception
        1. call **[Promise unhandled_exception]**
8. call **[Promise final_suspend]**, this function must be **noexcept**
9. call **[Handle destory]**
    1. destruct **[Promise]**
    2. destruct **[Function Params Copy]**
    3. delete **[Coroutine State]**
    4. transfers execution back to the caller/resumer

## Dynamic Allocate
1. replacement of **[Promise new operator]**, first argument is the size requested (of type std::size_t) and the rest are the coroutine function arguments
    1. no param or not matching **[default replacement]**
    2. matching **[replacement with params]**
2. call **[Promise get_return_object_on_allocation_failure]** when allocation failure

## co_await Behavior
1. find [Await Object]
    1. co_await [Await Object]
    2. co_await [Awaitable Object]
    3. co_await [Transfrom Object]
2. call **[Await await_ready]**, this function must return **bool**
    1. if true, won't suspend
3. call **[Await await_suspend]**, this function must input **std::coroutine_handle<TypePromise>**
    1. this function can return **void**
    2. this function can return **bool**, same as **await_ready**
    3. this function can return **std::coroutine_handle<TypeOtherPromise>**
    4. can throw exception
4. call **[Await await_resume]**
    1. this function can return **void**
    2. this function can return **any return value**

## co_yield Behavior
1. **[co_yield expr]** is same as **[co_await promise.yield_value(expr)]**
2. call **[yield_value]**, this function return an **awaiter**
    1. you need to get the **expr** manually
*/

template<typename T>
struct WithReturnObjectClass {
    using ROC = T;  // ROC means ReturnObjectClass
};

namespace CppReferenceStandard {
    namespace PromiseConstructor {
        class FExecutionReturnObjectClass: WithReturnObjectClass<FExecutionReturnObjectClass> {
        public:
            class FPromise {
                // Constructor
                public:
                    FPromise()                      { LOG_HERE("[PromiseConstructor: Default Constructor]        "); }                              // A Execution Behavior 3.2
                    FPromise(double)                { LOG_HERE("[PromiseConstructor: Custom Matching Constructor]"); }                              // A Execution Behavior 3.1
                    FPromise(std::string)           { bTestException = true; LOG_HERE("[PromiseConstructor: Exception Constructor      ]"); }       // A Execution Behavior 3.1
                private:
                    bool bTestException = false;                                                                                                    // A Execution Behavior 4.1
                // Coroutine Interface
                public:
                    std::suspend_never initial_suspend()                { return {}; }                                                              // A Execution Behavior 5
                    std::suspend_never final_suspend()   noexcept       { return {}; }                                                              // A Execution Behavior 8
                    void unhandled_exception()                          {}
                    ROC get_return_object()                             { if (bTestException) { throw "get_return_object Throw"; } return ROC(); }  // A Execution Behavior 4.1
                    void return_void()                                  {}                                                                          // A Execution Behavior 7
            };
            // using promise_type = FPromise;                           // A Execution Behavior 4 Find by std::coroutine_traits<CppReferenceStandard::PromiseConstructor::FExecutionReturnObjectClass, Args...>
        };
    }

    namespace Lifetime {
        class FExecutionReturnObjectClass_RetVoid: WithReturnObjectClass<FExecutionReturnObjectClass_RetVoid> {
        public:
            class FPromise {
                // Constructor
                public:
                    FPromise()                      { LOG_HERE("[Lifetime: Default Constructor]   "); }
                    ~FPromise()                     { LOG_HERE("[Lifetime: Destructor]   "); }
                // Coroutine Interface
                public:
                    std::suspend_never initial_suspend()                { LOG_HERE("[Lifetime: initial_suspend]       "); return {}; }      // A Execution Behavior 5
                    std::suspend_never final_suspend()   noexcept       { LOG_HERE("[Lifetime: final_suspend]         "); return {}; }      // A Execution Behavior 8
                    void unhandled_exception()                          { LOG_HERE("[Lifetime: unhandled_exception]   "); }
                    ROC get_return_object()                             { LOG_HERE("[Lifetime: get_return_object]     "); return ROC(); }   // A Execution Behavior 6.3
                    void return_void()                                  { LOG_HERE("[Lifetime: return_void]           "); }                 // A Execution Behavior 7
            };
            using promise_type = FPromise;                              // A Execution Behavior 4
        };

        class FExecutionReturnObjectClass_RetInt: WithReturnObjectClass<FExecutionReturnObjectClass_RetInt> {
        public:
            class FPromiseWithValue {
                // Constructor
                public:
                    FPromiseWithValue()                                 { LOG_HERE("[Lifetime: Default Constructor]   "); }
                    ~FPromiseWithValue()                                { LOG_HERE("[Lifetime: Destructor]   "); }
                // Coroutine Interface
                public:
                    std::suspend_never initial_suspend()                { LOG_HERE("[Lifetime: initial_suspend]       "); return {}; }       // A Execution Behavior 5
                    std::suspend_never final_suspend()   noexcept       { LOG_HERE("[Lifetime: final_suspend]         "); return {}; }       // A Execution Behavior 8
                    void unhandled_exception()                          { LOG_HERE("[Lifetime: unhandled_exception]   "); }
                    ROC get_return_object()                             { LOG_HERE("[Lifetime: get_return_object]     "); return ROC(); }    // A Execution Behavior 6.3
                    void return_value(int)                              { LOG_HERE("[Lifetime: return_value]          "); }                  // A Execution Behavior 7
            };
            using promise_type = FPromiseWithValue;                     // A Execution Behavior 4
        };
    }

    namespace DynamicAllocate {
        class FExecutionReturnObjectClass: WithReturnObjectClass<FExecutionReturnObjectClass> {
        public:
            class FPromise {
                // Constructor
                public:
                    FPromise()                                          { LOG_HERE("[DynamicAllocate: Default Constructor]   "); }
                    ~FPromise()                                         { LOG_HERE("[DynamicAllocate: Destructor]   "); }
                // Coroutine Interface
                public:
                    std::suspend_never initial_suspend()                { return {}; }
                    std::suspend_never final_suspend()   noexcept       { return {}; }
                    void unhandled_exception()                          { }
                    ROC get_return_object()                             { return ROC(); }
                    void return_void()                                  { }
                    static ROC get_return_object_on_allocation_failure() {                      // must be static?
                        LOG_HERE("[DynamicAllocate: get_return_object_on_allocation_failure] ");// yes, when allocation failure, there's no **object**! 
                        throw "get_return_object_on_allocation_failure Throw";
                    }
                // Replacement of new
                public:
                    void* operator new(std::size_t n) noexcept {                                // Dynamic Allocate 1.1
                        LOG_HERE("[DynamicAllocate: new] ");
                        if (void* mem = std::malloc(n)) return mem;
                        return nullptr;
                    }
                    void* operator new(std::size_t n, bool bForceError) noexcept {              // Dynamic Allocate 1.2
                        LOG_HERE("[DynamicAllocate: new bForceError] ");
                        if (!bForceError) if (void* mem = std::malloc(n)) return mem;
                        return nullptr;
                    }
            };
            using promise_type = FPromise;                              // A Execution Behavior 4
        };
    }

    namespace Awaiter {
        class FExecutionReturnObjectClass: WithReturnObjectClass<FExecutionReturnObjectClass> {
        public:
            class FAwaiter;
            class FAwaitable;

            class FPromise {
                // Constructor
                public:
                    FPromise()                  { LOG_HERE("[Await LiftTime: FPromise constructor]"); }
                    ~FPromise()                 { LOG_HERE("[Await LiftTime: FPromise destructor]"); }
                // Coroutine Interface
                public:
                    std::suspend_never initial_suspend()                { return {}; }
                    std::suspend_never final_suspend()   noexcept       { return {}; }
                    void unhandled_exception()                          { }
                    ROC get_return_object()                             { return ROC(); }
                    void return_void()                                  { }

                    template<class T>       // co_await Behavior 1.3
                    T&& await_transform(T&& AwaitObject)                { LOG_HERE("[Await LiftTime: await_transform]"); return std::move(AwaitObject); }
                    FAwaitable await_transform(double)                  { LOG_HERE("[Await LiftTime: await_transform]"); return FAwaitable{}; }
            };
            class FAwaiter {                // co_await Behavior 1.1
            public:
                FAwaiter()                                              { LOG_HERE("[Await LiftTime: FAwaiter constructor]"); }
                ~FAwaiter()                                             { LOG_HERE("[Await LiftTime: FAwaiter destructor]"); }
                // Coroutine Interface
                bool await_ready()                                      { LOG_HERE("[Await LiftTime: await_ready]"); return false; }        // co_await Behavior 2
                void await_suspend(std::coroutine_handle<FPromise> h)   { LOG_HERE("[Await LiftTime: await_suspend]"); h.resume(); }
                void await_resume()                                     { LOG_HERE("[Await LiftTime: await_resume]"); }                     // co_await Behavior 4.1
            };
            class FAwaitable {              // co_await Behavior 1.2
            public:
                FAwaitable()                    { LOG_HERE("[Await LiftTime: FAwaitable constructor]"); }
                ~FAwaitable()                   { LOG_HERE("[Await LiftTime: FAwaitable destructor]"); }
                FAwaiter operator co_await()    { LOG_HERE("[Await LiftTime: override co_await]"); return FAwaiter{}; };
            };
            class FAwaiterReturnValue {
            public: 
                bool bThrow;
            public:
                // Coroutine Interface
                bool await_ready()                                      { return false; }
                void await_suspend(std::coroutine_handle<FPromise> h)   { if (bThrow) { throw "await_suspend throw"; } h.resume(); }        // co_await Behavior 3.4
                int await_resume()                                      { return 32; }                                                      // co_await Behavior 4.2
            };
            using promise_type = FPromise;
        };
    }

    namespace Yield {
        class FExecutionReturnObjectClass: WithReturnObjectClass<FExecutionReturnObjectClass> {
        public:
            class FPromise {
                // Constructor
                public:
                    FPromise()                  { }
                    ~FPromise()                 { }
                // Coroutine Interface
                public:
                    std::suspend_always initial_suspend()               { return {}; }      // suspend_always
                    std::suspend_always final_suspend()   noexcept      { return {}; }      // suspend_always
                    void unhandled_exception()                          { }
                    ROC get_return_object()                             { return ROC(handle_type::from_promise(*this)); }
                    void return_void()                                  { }
                    std::suspend_always yield_value(int InValue)        { LOG_HERE("[Yield: yield_value]"); _bSetValue = true; _Value = InValue; return {}; } // co_yield Behavior 1
                public:
                    int _Value          = 0;                            // Store Calculation Result
                    int GetValue()      { return _Value; }
                    bool _bSetValue     = false;
                    bool CanGetValue()  { return _bSetValue; }
            };
        public:
            using handle_type  = std::coroutine_handle<FPromise>;
            class FAwaiterGetHandle {
                handle_type _h;
                public:
                    bool await_ready() { return false; }
                    void await_suspend(handle_type h) { _h = h; h(); }  // Cache handle
                    handle_type& await_resume() { return _h; }
            };

            int operator()() {          // main thread get value
                if (_h.promise().CanGetValue()) {
                    _h.promise()._bSetValue = false;
                    return _h.promise().GetValue();                     // co_yield Behavior 2.1
                }
                throw "Error Get";
            }
            explicit operator bool() {  // **for** will call this
                _h();                   // this is resume, in std::coroutine_handle
                                        // void operator()() const {
                                        //     __builtin_coro_resume(_Ptr);
                                        // }
                return !_h.done();
            }
            using promise_type = FPromise;
        private:
            handle_type _h;
        public:
            FExecutionReturnObjectClass(handle_type h): _h(h) {}
            ~FExecutionReturnObjectClass() { _h.destroy(); }
        };
    }

    void Test();
}

template<typename... Args>
struct std::coroutine_traits<CppReferenceStandard::PromiseConstructor::FExecutionReturnObjectClass, Args...> {
    using promise_type = CppReferenceStandard::PromiseConstructor::FExecutionReturnObjectClass::FPromise;
};